package com.example.java_chatroom.api;

import com.example.java_chatroom.component.OnlineUserMapper;
import com.example.java_chatroom.model.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.List;

@Slf4j
@Component
public class WebSocketAPI extends TextWebSocketHandler {
    @Autowired
    private OnlineUserMapper onlineUserMapper;

    @Autowired
    private MessageSessionMapper messageSessionMapper;

    @Autowired
    private MessageMapper messageMapper;

    // 自己创建对象也行, 使用 @Autowired 注入也行, spring 本身就有内置对象 ObjectMapper
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("[WebSocketAPI] 连接成功!");
        User user = (User) session.getAttributes().get("user");
        if(user == null) {
            return;
        }
        log.info("获取到的 userId: {}, username: {}",user.getUserId(), user.getUsername());
        // 连接建立成功之后, 将 上线用户 和 session 进行绑定
        onlineUserMapper.online(user.getUserId(), session);
    }

    /**
     * 数据处理
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("[WebSocketAPI] 收到消息! " + message.toString());

        // 先获取到当前用户的信息, 后续要转发的消息等
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            log.info("[WebSocketAPI] user == null, 未登录用户, 无法进行消息转发");
            return;
        }

        // 针对请求进行解析, 把 json 格式字符串转换成 Java 对象
        MessageRequest req = objectMapper.readValue(message.getPayload(), MessageRequest.class);
        if("message".equals(req.getType())) {
            // 进行消息转发
            transferMessage(user, req);
        }else {
            log.info("[WebSocketAPI] req.type 有误! {}", message.getPayload());
        }
    }

    /**
     * 通过该方法来完成消息的实际转发过程
     * @param user 发送消息的对象
     * @param req 内含 sessionId, content
     */
    private void transferMessage(User user, MessageRequest req) throws IOException {
        // 先构造一个待转发的响应对象. MessageResponse
        MessageResponse resp = new MessageResponse(user.getUserId(), user.getUsername(), req.getSessionId(), req.getContent());

        // 把这个响应对象转换成 JSON 格式字符串,以待备用
        String respJson = objectMapper.writeValueAsString(resp);
        log.info("[transferMessage] respJson: {}", respJson);

        // 根据请求中的 sessionId, 获取到 MessageSession 里有哪些用户 (查询数据库)
        List<Friend> friends =  messageSessionMapper.getFriendsBySessionId(req.getSessionId(), user.getUserId());
        // 此处响应返回的对象中, 应该包含发送方
        Friend myself = new Friend(user.getUserId(), user.getUsername());
        friends.add(myself);

        // 循环遍历 friends, 给其中每一个对象都发送一份响应
        //   这里是为了满足群聊的设定(即使前端还未实现,但是后端接口和数据库都是支持群聊的)
        for(Friend friend : friends) {
            // 已知 userId, 进一步查询 OnlineUserMapper, 获取对应的 WebSocketSession, 从而进行消息转发
            WebSocketSession webSocketSession = onlineUserMapper.getSession(friend.getFriendId());
            if(webSocketSession != null) {
                webSocketSession.sendMessage(new TextMessage(respJson));
            }
        }

        // 转发的消息还要在数据库备份
        Message message = new Message(user.getUserId(), user.getUsername(), req.getSessionId(), resp.getContent());
        // 自增主键为 null或为空, 数据库会自动生成
        messageMapper.add(message);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.info("[WebSocketAPI] 连接异常! " + exception.toString());
        User user = (User) session.getAttributes().get("user");
        if(user != null) {
            onlineUserMapper.offline(user.getUserId(), session);
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        log.info("[WebSocketAPI] 连接关闭! " + status.toString());
        User user = (User) session.getAttributes().get("user");
        if(user != null) {
            onlineUserMapper.offline(user.getUserId(), session);
        }
    }
}
